//
// Created by WINDOWS on 2025/6/18.
//

#include "FileSpaceWatcher.h"
#include <filesystem>
#include <iostream>
#include <system_error>
#include <cstring>
#include <algorithm>
#include <queue>

namespace fs = std::filesystem;

FileSpaceWatcher::FileSpaceWatcher()
        : m_running(false),
          m_largeFileThreshold(10 * 1024 * 1024), // 默认10MB为大文件
          m_frequentUpdateThreshold(1000) {       // 默认1秒内多次更新为频繁文件
#ifdef _WIN32
    m_directoryHandle = INVALID_HANDLE_VALUE;
    memset(&m_overlapped, 0, sizeof(m_overlapped));
    m_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    m_buffer.resize(64  * 1024); // 64KB buffer
#else
    m_inotifyFd = inotify_init1(IN_NONBLOCK);
    if (m_inotifyFd == -1) {
        throw std::runtime_error("Failed to initialize inotify");
    }
#endif
}

FileSpaceWatcher::~FileSpaceWatcher() {
    stop();
#ifdef _WIN32
    CloseHandle(m_overlapped.hEvent);
    for (auto& dir : m_watchedDirectories) {
        CloseHandle(dir.second);
    }
#else
    close(m_inotifyFd);
#endif
}

bool FileSpaceWatcher::addWatch(const std::string& path, bool recursive) {
    std::lock_guard<std::mutex> lock(m_mutex);

    try {
        if (!fs::exists(path) || !fs::is_directory(path)) {
            return false;
        }

#ifdef _WIN32
        HANDLE handle = CreateFileA(
                path.c_str(),
                FILE_LIST_DIRECTORY,
                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                NULL,
                OPEN_EXISTING,
                FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
                NULL
        );

        if (handle == INVALID_HANDLE_VALUE) {
            return false;
        }

        m_watchedDirectories[path] = handle;
#else
        int wd = inotify_add_watch(m_inotifyFd, path.c_str(),
                                  IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO);
        if (wd == -1) {
            return false;
        }
        m_watchDescriptors[wd] = path;
#endif

        // 初始扫描目录
        scanDirectory(path, recursive);
        return true;
    } catch (const std::exception& e) {
        std::cerr << "Error adding watch: " << e.what()  << std::endl;
        return false;
    }
}

bool FileSpaceWatcher::removeWatch(const std::string& path) {
    std::lock_guard<std::mutex> lock(m_mutex);

#ifdef _WIN32
    auto it = m_watchedDirectories.find(path);
    if (it != m_watchedDirectories.end())  {
        CloseHandle(it->second);
        m_watchedDirectories.erase(it);
        return true;
    }
#else
    for (auto it = m_watchDescriptors.begin();  it != m_watchDescriptors.end();  ) {
        if (it->second == path) {
            inotify_rm_watch(m_inotifyFd, it->first);
            it = m_watchDescriptors.erase(it);
            return true;
        } else {
            ++it;
        }
    }
#endif

    return false;
}

uint64_t FileSpaceWatcher::getDirectorySize(const std::string& path) {
    std::lock_guard<std::mutex> lock(m_mutex);
    uint64_t totalSize = 0;

    for (const auto& [filepath, info] : m_fileStats) {
        if (filepath.find(path)  == 0) {
            totalSize += info.size;
        }
    }

    return totalSize;
}

const std::unordered_map<std::string, FileInfo>& FileSpaceWatcher::getFileStats() const {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_fileStats;
}

void FileSpaceWatcher::setUpdateCallback(UpdateCallback callback) {
    m_callback = callback;
}

void FileSpaceWatcher::start() {
    if (m_running) return;

    m_running = true;
    m_monitorThread = std::thread(&FileSpaceWatcher::monitorThreadFunc, this);
}

void FileSpaceWatcher::stop() {
    if (!m_running) return;

    m_running = false;
    if (m_monitorThread.joinable())  {
        m_monitorThread.join();
    }
}

void FileSpaceWatcher::setLargeFileThreshold(uint64_t threshold) {
    m_largeFileThreshold = threshold;
}

void FileSpaceWatcher::setFrequentUpdateThreshold(int milliseconds) {
    m_frequentUpdateThreshold = milliseconds;
}

void FileSpaceWatcher::monitorThreadFunc() {
    std::cout << "FileSpaceWatcher monitor thread started" << std::endl;
#ifdef _WIN32
    std::vector<HANDLE> handles;
    for (const auto& dir : m_watchedDirectories) {
        handles.push_back(dir.second);
    }

    while (m_running) {
        DWORD bytesReturned = 0;

        for (auto& dir : m_watchedDirectories) {
            if (!ReadDirectoryChangesW(
                    dir.second,
                    m_buffer.data(),
                    static_cast<DWORD>(m_buffer.size()),
                    TRUE,
                    FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
                    FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE |
                    FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SECURITY,
                    &bytesReturned,
                    &m_overlapped,
                    NULL)) {
                continue;
            }

            DWORD waitResult = WaitForSingleObject(m_overlapped.hEvent, 100);
            if (waitResult == WAIT_OBJECT_0) {
                if (GetOverlappedResult(dir.second,  &m_overlapped, &bytesReturned, FALSE)) {
                    FILE_NOTIFY_INFORMATION* notifyInfo = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(m_buffer.data());

                    while (true) {
                        std::wstring filenameW(notifyInfo->FileName, notifyInfo->FileNameLength / sizeof(WCHAR));
                        std::string filename(filenameW.begin(),  filenameW.end());
                        std::string fullpath = dir.first  + "\\" + filename;
                        std::cout << "File event: " << fullpath << " (" << notifyInfo->Action << ")" << std::endl;

                        switch (notifyInfo->Action) {
                            case FILE_ACTION_ADDED:
                            case FILE_ACTION_MODIFIED:
                            case FILE_ACTION_RENAMED_NEW_NAME:
                                processFileEvent(fullpath);
                                break;
                            case FILE_ACTION_REMOVED:
                            case FILE_ACTION_RENAMED_OLD_NAME: {
                                std::lock_guard<std::mutex> lock(m_mutex);
                                m_fileStats.erase(fullpath);
                                if (m_callback) {
                                    m_callback(fullpath, 0);
                                }
                                break;
                            }
                        }

                        if (notifyInfo->NextEntryOffset == 0) {
                            break;
                        }

                        notifyInfo = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(
                                reinterpret_cast<BYTE*>(notifyInfo) + notifyInfo->NextEntryOffset);
                    }
                }

                ResetEvent(m_overlapped.hEvent);
            }
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
#else
    const int EVENT_SIZE = sizeof(inotify_event);
    const int BUF_LEN = 1024 * (EVENT_SIZE + 16);
    char buffer[BUF_LEN];

    while (m_running) {
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(m_inotifyFd, &fds);

        struct timeval timeout;
        timeout.tv_sec  = 1;
        timeout.tv_usec  = 0;

        int ready = select(m_inotifyFd + 1, &fds, NULL, NULL, &timeout);
        if (ready <= 0) {
            continue;
        }

        int length = read(m_inotifyFd, buffer, BUF_LEN);
        if (length < 0) {
            continue;
        }

        int i = 0;
        while (i < length) {
            struct inotify_event* event = reinterpret_cast<struct inotify_event*>(&buffer[i]);
            if (event->len) {
                std::string dirPath = m_watchDescriptors[event->wd];
                std::string filepath = dirPath + "/" + event->name;
                std::cout << "File event: " << filepath << " (" << event->mask << ")" << std::endl;

                if (event->mask & IN_CREATE || event->mask & IN_MODIFY || event->mask & IN_MOVED_TO) {
                    processFileEvent(filepath);
                } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
                    std::lock_guard<std::mutex> lock(m_mutex);
                    m_fileStats.erase(filepath);
                    if (m_callback) {
                        m_callback(filepath, 0);
                    }
                }
            }
            i += EVENT_SIZE + event->len;
        }
    }
#endif
}

void FileSpaceWatcher::scanDirectory(const std::string& path, bool recursive) {
    try {
        std::vector<std::thread> workers;
        std::mutex scanMutex;
        std::queue<fs::path> dirsToScan;
        dirsToScan.push(path);

        auto workerFunc = [&]() {
            while (true) {
                fs::path currentDir;
                {
                    std::lock_guard<std::mutex> lock(scanMutex);
                    if (dirsToScan.empty())  {
                        break;
                    }
                    currentDir = dirsToScan.front();
                    dirsToScan.pop();
                }

                try {
                    for (const auto& entry : fs::directory_iterator(currentDir)) {
                        try {
                            if (entry.is_directory())  {
                                if (recursive) {
                                    std::lock_guard<std::mutex> lock(scanMutex);
                                    dirsToScan.push(entry.path());
                                    std::cout << "Scanning directory: " << entry.path() << std::endl;
                                }
                            } else if (entry.is_regular_file())  {
                                std::string filepath = entry.path().string();
                                uint64_t fileSize = entry.file_size();
                                std::time_t lastModified = fs::last_write_time(entry).time_since_epoch().count();
//                                std::lock_guard<std::mutex> lock(m_mutex);
                                std::lock_guard<std::mutex> lock(scanMutex);
                                m_fileStats[filepath] = {fileSize, lastModified};
                                std::cout << "Scanning file: " << filepath << " (" << fileSize / (1024 * 1024) << " MB)" << std::endl;

                                if (fileSize > m_largeFileThreshold) {
                                    handleLargeFile(filepath);
                                }

                                if (m_callback) {
                                    m_callback(filepath, fileSize);
                                }
                            }
                        } catch (const std::exception& e) {
                            std::cerr << "Error processing " << entry.path()  << ": " << e.what()  << std::endl;
                        }
                    }
                } catch (const std::exception& e) {
                    std::cerr << "Error scanning " << currentDir << ": " << e.what()  << std::endl;
                }
            }
        };

        // 根据CPU核心数创建线程
        unsigned int numThreads = std::thread::hardware_concurrency();
        if (numThreads == 0) numThreads = 4;

        for (unsigned int i = 0; i < numThreads; ++i) {
            workers.emplace_back(workerFunc);
        }

        for (auto& worker : workers) {
            if (worker.joinable())  {
                worker.join();
            }
        }
    } catch (const std::exception& e) {
        std::cerr << "Error scanning directory: " << e.what()  << std::endl;
    }
}

void FileSpaceWatcher::processFileEvent(const std::string& filepath) {
    try {
        auto now = std::chrono::steady_clock::now();

        // 检查是否是频繁更新的文件
        {
            auto it = m_frequentFiles.find(filepath);
            if (it != m_frequentFiles.end())  {
                auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - it->second).count();
                if (elapsed < m_frequentUpdateThreshold) {
                    // 文件更新过于频繁，暂时跳过
                    return;
                }
            }
            m_frequentFiles[filepath] = now;
        }

        // 清理过期的频繁文件记录
        if (m_frequentFiles.size()  > 1000) { // 防止内存泄漏
            for (auto it = m_frequentFiles.begin();  it != m_frequentFiles.end();  ) {
                auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - it->second).count();
                if (elapsed > m_frequentUpdateThreshold * 10) {
                    it = m_frequentFiles.erase(it);
                } else {
                    ++it;
                }
            }
        }

        fs::path path(filepath);
        if (fs::exists(path) && fs::is_regular_file(path)) {
            uint64_t fileSize = fs::file_size(path);
            std::time_t lastModified = fs::last_write_time(path).time_since_epoch().count();

            std::lock_guard<std::mutex> lock(m_mutex);
            auto& info = m_fileStats[filepath];
            uint64_t oldSize = info.size;
            info.size  = fileSize;
            info.last_modified  = lastModified;

            if (fileSize > m_largeFileThreshold) {
                handleLargeFile(filepath);
            }

            if (m_callback && oldSize != fileSize) {
                m_callback(filepath, fileSize);
            }
        }
    } catch (const std::exception& e) {
        std::cerr << "Error processing file event: " << e.what()  << std::endl;
    }
}

void FileSpaceWatcher::handleLargeFile(const std::string& filepath) {
    // 对大文件的特殊处理，例如记录日志或触发特定回调
    std::cout << "Large file detected: " << filepath
              << " (" << m_fileStats[filepath].size / (1024 * 1024) << " MB)" << std::endl;
}